package com.chuangjiangx.member.coupon.ddd.domain.model;

import com.chuangjiangx.commons.BeanUtils;
import com.chuangjiangx.commons.exception.BaseException;
import com.chuangjiangx.dddbase.Entity;
import com.chuangjiangx.dddbase.domain.shared.Timestamp;
import com.chuangjiangx.member.stored.ddd.domain.MbrRandomUtils;
import com.chuangjiangx.member.basic.ddd.domain.model.MerchantId;
import lombok.Getter;
import org.apache.commons.lang3.time.DateUtils;

import java.math.BigDecimal;
import java.time.*;
import java.util.*;

/**
 * 会员卡券
 */
@Getter
public class MbrCoupon extends Entity<MbrCouponId> {
    private String couponNumber; //卡券编号
    private MerchantId merchantId;
    private String name; //名称
    private BigDecimal amount; //卡券面值、折扣
    private MbrCouponType type; //卡券类型
    private String logo; //卡券logo
    private MbrCouponSourceType couponSourceType;//卡券来源
    private ValidType validType; //卡券使用有效期类型
    private Date validTimeStart; //使用有效期开始
    private Date validTimeEnd; //使用有效期结束
    private String wxColor;//微信卡券颜色
    private String syncType;//是否同步至卡包
    private String wxCenterSubTitle;//微信子标题
    private WxAuditStatus wxAuditStatus;//审核状态 0未审核 1审核通过 2审核失败
    private Integer claimedTime; //领取后有效XX天
    private Long totalInventory; //总库存
    private Long availInventory; //可用库存
    private String remark; //说明
    private BigDecimal miniExpendLimit; //使用最低消费限制
    private Integer claimUpperLimit; //每人领取上限
    private InvalidRemindType remindType; //到期提醒类型
    private String useTimeWeek; //使用时段-周
    private String useTimeDay; //使用时段-天
    private Date actTimeStart; //领取卡券活动开始
    private Date actTimeEnd; //领取活动结束、
    private String wxCardId;//微信卡券id
    private String wxRefuseReason;//微信拒绝理由
    private Date createTime;//创建时间
    private Date updateTime;//
    private MbrCoupon(){}

    /**
     * 创建卡券
     */
    public MbrCoupon(String couponNumber, MerchantId merchantId, String name,
                     BigDecimal amount, MbrCouponType type, String logo,
                     ValidType validType, Date validTimeStart, Date validTimeEnd,
                     Integer claimedTime, Long totalInventory, Long availInventory,
                     String remark, BigDecimal miniExpendLimit, Integer claimUpperLimit,
                     InvalidRemindType remindType, String useTimeWeek, String useTimeDay,
                     MbrCouponSourceType couponSourceType, Date actTimeStart, Date actTimeEnd,
                     String wxColor,String syncType,String wxCenterSubTitle,WxAuditStatus wxAuditStatus,
                     String wxCardId,String wxRefuseReason
                     ) {
        this.wxColor = wxColor;
        this.wxAuditStatus = wxAuditStatus;
        this.syncType = syncType;
        this.wxCenterSubTitle = wxCenterSubTitle;
        this.couponNumber = couponNumber;
        this.merchantId = merchantId;
        this.name = name;
        this.amount = amount;
        this.type = type;
        this.logo = logo;
        this.validType = validType;
        this.validTimeStart = validTimeStart;
        this.validTimeEnd = validTimeEnd;
        this.claimedTime = claimedTime;
        this.totalInventory = totalInventory;
        this.availInventory = availInventory;
        this.remark = remark;
        this.miniExpendLimit = miniExpendLimit;
        this.claimUpperLimit = claimUpperLimit;
        this.remindType = remindType;
        this.useTimeWeek = useTimeWeek;
        this.useTimeDay = useTimeDay;
        this.couponSourceType = couponSourceType;
        this.actTimeStart = actTimeStart;
        this.actTimeEnd = actTimeEnd;
        this.wxCardId = wxCardId;
        this.wxRefuseReason = wxRefuseReason;
    }

    /**
     * 更新卡券
     */
    public void updateCoupon(
            String name, BigDecimal amount, MbrCouponType type,MbrCouponSourceType couponSourceType,
            String logo, BigDecimal miniExpendLimit, Integer claimUpperLimit,Long totalInventory,
            ValidType validType,Integer claimedTime, Date validTimeStart, Date validTimeEnd,
            String useTimeWeek, String useTimeDay, InvalidRemindType remindType,Date actTimeStart,
            String remark, Date actTimeEnd , String wxColor,String syncType,String wxCenterSubTitle) {
        if (new Date().after(this.actTimeStart)) {
            throw new IllegalArgumentException("卡券活动已开始，无法修改");
        }
        this.name = name;
        this.amount = amount;
        this.type = type;
        this.logo = logo;
        this.validType = validType;
        this.validTimeStart = validTimeStart;
        this.validTimeEnd = validTimeEnd;
        this.claimedTime = claimedTime;
        this.totalInventory = totalInventory;
        this.availInventory = totalInventory;
        this.remark = remark;
        this.miniExpendLimit = miniExpendLimit;
        this.claimUpperLimit = claimUpperLimit;
        this.remindType = remindType;
        this.useTimeWeek = useTimeWeek;
        this.useTimeDay = useTimeDay;
        this.couponSourceType = couponSourceType;
        this.actTimeStart = actTimeStart;
        this.actTimeEnd = actTimeEnd;
        this.update();
        this.wxColor = wxColor;
        this.syncType = syncType;
        this.wxCenterSubTitle = wxCenterSubTitle;
    }

    /**
     * 库存操作校验
     * @param type 1-增 2-减
     */
    public void checkInventory(Integer type,Long amount) {
        if (new Date().after(this.actTimeEnd)) {
            throw new IllegalArgumentException("卡券活动已结束");
        }
        switch (type) {
            case 1 :
                break;
            case 2 :
                if (this.availInventory < amount) {
                    throw new IllegalArgumentException("卡券库存不足");
                }
                break;
            default:
                throw new IllegalArgumentException("错误的库存操作");
        }
    }

    /**
     * 立即结束活动
     */
    public void finishActivity() {
        Date now = new Date();
        if (this.actTimeEnd.before(now)) {
            throw new IllegalArgumentException("卡券活动已结束");
        }
        this.actTimeEnd = now;
        this.update();
    }

    /**
     * 复制卡券
     */
    public MbrCoupon copyMbrCoupon() {
        MbrCoupon replication = new MbrCoupon();
        BeanUtils.convertBean(this,replication);
        replication.couponNumber = genCouponNumber();
        /*replication.actTimeEnd = this.actTimeEnd;
        replication.merchantId = this.merchantId;
        replication.name = this.name;
        replication.amount = this.amount;
        replication.type = this.type;
        replication.logo = this.logo;
        replication.validType = this.validType;
        replication.validTimeStart = this.validTimeStart;
        replication.validTimeEnd = this.validTimeEnd;
        replication.totalInventory = this.totalInventory;
        replication.availInventory = this.totalInventory;
        replication.remark = this.remark;
        replication.miniExpendLimit = this.miniExpendLimit;
        replication.claimUpperLimit = this.claimUpperLimit;
        replication.remindType = this.remindType;
        replication.useTimeWeek = this.useTimeWeek;
        replication.useTimeDay = this.useTimeDay;
        replication.couponSourceType = this.couponSourceType;
        replication.actTimeStart = this.actTimeStart;
        replication.actTimeEnd = this.actTimeEnd;*/
        replication.setTimestamp(new Timestamp(new Date(),new Date()));
        return replication;
    }

    /**
     * 校验卡券核销
     */
    public void checkVerifyCode(BigDecimal orderAmount, Date claimTime) {
        //2.校验最低消费限制
        checkMiniExpendLimit(orderAmount);
        //3.校验使用时段
        checkUseTime();
        //1.校验有效期
        boolean r = checkValidUseDate(claimTime);
        if(!r){
            throw new BaseException("","卡券不在使用有效期内");
        }
    }

    /**
     * 卡券使用有效期校验
     * @param claimTime 会员卡券领取时间
     * @return true-未过期 false-已过期
     */
    private boolean checkValidUseDate(Date claimTime) {
        Date now = new Date();
        switch (this.validType) {
            case EFFECT_DAYS:
                //领取后有效XX天
                Date endDate = DateUtils.addDays(claimTime, this.claimedTime);
                return DateUtils.truncatedCompareTo(endDate,now,Calendar.DATE) >= 0;
            case FIXED_DATE:
                //固定时间
                //当前时间在有效期内
                return DateUtils.truncatedCompareTo(this.validTimeEnd,now,Calendar.DATE) >= 0
                        &&
                        DateUtils.truncatedCompareTo(now,this.validTimeStart,Calendar.DATE) >= 0;
            default:
                throw new IllegalArgumentException("未知卡券有效期类型");
        }
    }

    /**
     * 卡券领取有效期验证
     */
    public void claimCheck() {
        if(Objects.equals(MbrCouponSourceType.CLAIMED,this.couponSourceType)){
            Date now = new Date();
            if (!(now.after(this.actTimeStart) && now.before(this.actTimeEnd))) {
                throw new IllegalArgumentException("卡券不在活动有效期内");
            }
        }
    }

    /**
     * 校验最低消费
     * @param orderAmount 订单金额
     */
    private void checkMiniExpendLimit(BigDecimal orderAmount) {
        if (this.miniExpendLimit.compareTo(orderAmount) > 0) {
            throw new IllegalArgumentException("订单金额未达到最低消费");
        }

    }

    /**
     * 校验使用时段
     */
    private void checkUseTime() {
        LocalDateTime now = LocalDateTime.now();
        DayOfWeek day = now.getDayOfWeek();
        List<String> dayOfWeek = Arrays.asList(this.useTimeWeek.split(","));
        if (! dayOfWeek.contains(day.getValue() + "")) {
            throw new IllegalArgumentException("当前时段无法使用该卡券");
        }
        List<String> useHours = Arrays.asList(this.useTimeDay.split(","));
        for (String useHour : useHours) {
            String[] hours = useHour.split("-");
            LocalTime start = LocalTime.parse(hours[0]);
            LocalTime end = LocalTime.parse(hours[1]);
            LocalTime localTime = now.toLocalTime();
            if (localTime.isAfter(start) && localTime.isBefore(end)) {
                return;
            }
        }
        throw new IllegalArgumentException("当前时段无法使用该卡券");
    }

    /**
     * 计算卡券抵扣金额
     * @param orderAmount
     * @return
     */
    public BigDecimal calcDiscountAmount(BigDecimal orderAmount){
        switch (type){
            case CASH_COUPON:
                //比较订单金额和代金券金额
                if(orderAmount.compareTo(amount) < 0){
                    return orderAmount;
                }
                return amount;
            case ONSALE_COUPONS:
                //TODO
                break;
            case EXCHANGE_COUPON:
                break;
            default:
                break;
        }
        return BigDecimal.ZERO;
    }

    /**
     * 计算卡券优惠后实际需要付款的金额
     * @param orderAmount
     * @return
     */
    public BigDecimal calcRealPayAmount(BigDecimal orderAmount){

        return orderAmount.subtract(calcDiscountAmount(orderAmount));
    }



    /**
     * 生成卡券编号
     * @return 编号
     */
    public static String genCouponNumber() {

        return MbrRandomUtils.generateMbrCouponNumber();
    }


    /**
     * 同步微信成功更新微信卡券id
     * @Param wxCardId 微信卡券id
     */
    public void syncWx(String wxCardId) {
        this.wxCardId = wxCardId;
    }

    /**
     * 微信提审
     */
    public void auditWx() {
        this.wxAuditStatus = WxAuditStatus.NOT_AUDITED;
    }

    /**
     * 微信审核通过
     */
    public void wxAuditSuccess() {
        this.wxAuditStatus = WxAuditStatus.SUCCESS;
    }

    /**
     * 微信审核失败
     */
    public void wxAuditFailed(String refuseReason) {
        this.wxAuditStatus = WxAuditStatus.FAILED;
        this.wxRefuseReason = refuseReason;
    }

    /**
     * 是否同步了微信
     */
    public boolean isSyncedWx() {
        if(this.syncType == null||"".equals(this.syncType)){
            return false;
        }
        String[] syncs = this.syncType.split(",");
        //同步类型中包含微信（1） 并且微信的cardId有值
        return Arrays.asList(syncs).contains("1")&&this.wxCardId!=null&&!"".equals(this.wxCardId);
    }
}
